package com.pansoft.openplanet.util;

import android.content.Context;
import android.util.Log;

import com.efounder.chat.http.JFCommonRequestManager;
import com.efounder.utils.ResStringUtil;
import com.pansoft.openplanet.R;
import com.pansoft.openplanet.bean.Account;
import com.utilcode.util.LogUtils;

import net.sf.json.JSONObject;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.WalletUtils;
import org.web3j.tx.Contract;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Response;

/**
 * 以太坊代币交易管理
 *
 * @author YQS
 */
public class EthTokenTransactionManager {
    private static final String TAG = "EthTokenTransactionManager";

    //    public static final BigInteger GAS_PRICE = BigInteger.valueOf(10_000_000_000L);
//    public static final BigInteger GAS = BigInteger.valueOf(370_107);
    private Context mContext;

    //能量币chainid
    public static String ENERGER_COIN_CHAINID = "4";
    //以太坊chainid
    public static String ETH_COIN_CHAINID = "2";
    //ETE
    public static String ETE_COIN = "136";

    private CompositeDisposable compositeDisposable;

    public void addDisposable(Disposable disposable) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        if (disposable != null) {
            compositeDisposable.add(disposable);
        }

    }

    /**
     * 加载Credentials 回调
     */
    public interface LoadCredentialsCallBack {
        void loadResult(boolean isSuccess, Credentials credentials);
    }

    /**
     * 交易结果回调
     */
    public interface TransactionCallBack<T> {
        void transactionResult(boolean isSuccess, T result);
    }

    /**
     * 交易次数回调
     */
    public interface NonceCallBack {
        void nonceResult(boolean isSuccess, BigInteger nonce);
    }

    public void dispose() {
        if (compositeDisposable != null) {
            compositeDisposable.dispose();
        }
    }

    public EthTokenTransactionManager(Context mContext) {
        this.mContext = mContext;
    }


    /**
     * 加载credentials
     *
     * @param filePath
     * @return
     */
    public Credentials loadCredentialsSync(String filePath) {
        Credentials credentials = null;
        try {
            credentials = WalletUtils.loadCredentials("", filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return credentials;
    }

    /**
     * 加载credentials(异步)
     *
     * @param filePath
     * @return
     */
    public void loadCredentialsASync(final String filePath, final LoadCredentialsCallBack callBack) {

        Disposable disposable = Observable.create(new ObservableOnSubscribe<Credentials>() {
            @Override
            public void subscribe(ObservableEmitter<Credentials> emitter) throws Exception {
                Credentials credentials = loadCredentialsSync(filePath);
                emitter.onNext(credentials);
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Credentials>() {
                    @Override
                    public void accept(Credentials credentials) throws Exception {
                        callBack.loadResult(true, credentials);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        callBack.loadResult(false, null);
                    }
                });
        addDisposable(disposable);
    }

    /**
     * 加载credentials(异步)
     *
     * @param privateKey 私钥
     * @return
     */
    public void loadCredentialsByPrvkeyASync(final String privateKey, final LoadCredentialsCallBack callBack) {

        Disposable disposable = Observable.create(new ObservableOnSubscribe<Credentials>() {
            @Override
            public void subscribe(ObservableEmitter<Credentials> emitter) throws Exception {
                Credentials credentials = Credentials.create(privateKey);
                emitter.onNext(credentials);
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Credentials>() {
                    @Override
                    public void accept(Credentials credentials) throws Exception {
                        callBack.loadResult(true, credentials);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        callBack.loadResult(false, null);
                    }
                });
        addDisposable(disposable);
    }

    /**
     * 加载credentials(异步)
     *
     * @param account(账户中带着加密的私钥)
     * @return
     */
    public void loadCredentialsASync(final Account account, final LoadCredentialsCallBack callBack) {
        //如果keystore 文件不为空
        if (account.getFileName() != null && !"".equals(account.getFileName())) {
            loadCredentialsASync(account.getFileName(), callBack);
            return;
        }
        TCAccountManager tcAccountManager = new TCAccountManager(mContext);
        List<Account> list = new ArrayList<>();
        list.add(account);
        tcAccountManager.restoreByRXjava(list, new TCAccountManager.AccountManagerListener() {
            @Override
            public void onSuccess(Object object) {
                String filePath = account.getFileName();
                if (filePath == null || "".equals(filePath)) {
                    callBack.loadResult(false, null);
                } else {
                    loadCredentialsASync(filePath, callBack);
                }
            }

            @Override
            public void onFail(String fail) {
                callBack.loadResult(false, null);
            }
        });
    }

    /**
     * ETH 转账(默认矿工费)（非代币）
     *
     * @param nonce       交易次数
     * @param fromAddress 转账地址
     * @param fromUserId  转账人imId
     * @param toAddress   收账地址
     * @param toUserId    收账人ID
     * @param amount      转账金额
     * @param credentials 证书
     * @param chainId     以太坊 2   能量币转账是4
     */
//    public void startEthTransaction(BigInteger nonce, String fromAddress,
//                                    String fromUserId, String toAddress,
//                                    String toUserId, BigInteger amount,
//                                    Credentials credentials, String chainId, TransactionCallBack callBack) {
//        startEthTransaction(nonce, fromAddress, fromUserId, toAddress, toUserId, amount, credentials, chainId,
//                ManagedTransaction.GAS_PRICE, Contract.GAS_LIMIT, callBack);
//    }

    /**
     * ETH 转账（普通转账 非智能合约）
     *
     * @param nonce       交易次数
     * @param fromAddress 转账地址
     * @param fromUserId  转账人imId
     * @param toAddress   收账地址
     * @param toUserId    收账人ID
     * @param amount      转账金额
     * @param credentials
     * @param chainId     以太坊 2   能量币转账是4
     * @param gasPrice    矿工费
     * @param gasLimit
     */
    public void startEthTransaction(BigInteger nonce, String fromAddress,
                                    String fromUserId, String toAddress,
                                    String toUserId, BigInteger amount,
                                    Credentials credentials, String chainId, BigInteger gasPrice,
                                    BigInteger gasLimit, final TransactionCallBack callBack) {
        String hexValue = null;
        try {
            toAddress = toAddress.toLowerCase();

           // //todo 之前手续费太低，现在手续费需要乘以70倍左右,跟imtoken 对应
//            gasPrice = gasPrice.multiply(new BigInteger("100"));
//            BigInteger gasLimit1 = new BigInteger(Contract.GAS_LIMIT.longValue() / 100 + "");

            Log.e("yqs",gasPrice.toString());


            //创建交易
            RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice,
                    gasLimit, toAddress, amount);
            //签名Transaction，这里要对交易做签名
            byte[] signedMessage;
            //ete转账需要额外传链编号
            if (chainId.equals(ETE_COIN)) {
                signedMessage = TransactionEncoder.signMessage(rawTransaction, 136, credentials);
            } else {
                signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            }
            hexValue = Numeric.toHexString(signedMessage);
            LogUtils.i("交易签名：" + hexValue);
        } catch (Exception e) {
            e.printStackTrace();
            if (callBack != null) {
                callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
            }
            return;
        }
        //接口调用参数
        HashMap<String, String> map = new HashMap<>();
        map.put("fromUserId", fromUserId);
        map.put("fromEthAddress", fromAddress);
        map.put("toUserId", toUserId);
        map.put("toEthAddress", toAddress);
        map.put("value", Convert.fromWei(amount.toString(), Convert.Unit.ETHER).toString());
        map.put("signedTransactionData", hexValue);
        map.put("type", "1");//1 普通用户间的转账
        map.put("chainId", chainId);//Tuborchain的有关操作  chainId都要传4   chainId=2 是以太坊公网的

        LogUtils.i("amount:" + amount.toString() + "   " + Convert.fromWei(amount.toString(), Convert.Unit.ETHER).toString()
                , map.toString());
        //调用接口进行交易
        TCRequestUtil.postCommonRequest(TAG, "chain/ethSendRawTransaction",
                map, new TCRequestUtil.TCRequestCallback() {
                    @Override
                    public void onSuccess(String response) {

                        try {
                            JSONObject jsonObject = JSONObject.fromObject(response);
                            LogUtils.i("startEthTransaction" + jsonObject.toString());
                            if (jsonObject.containsKey("result") && "success".equals(jsonObject.getString("result"))) {
                                if (jsonObject.has("transactionHash")) {
                                    //交易成功，返回交易编号
                                    callBack.transactionResult(true, jsonObject.toString());
                                } else {
                                    callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                                }
                            } else {
                                callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                        }
                    }

                    @Override
                    public void onFail(String error) {
                        callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                    }
                });
    }

    /**
     * 获取账号交易次数 nonce
     *
     * @param fromAddress 付款地址
     * @param chainID     链id
     */
    public void getTransactionNonce(String fromAddress, int chainID, final NonceCallBack callBack) {
        HashMap<String, String> map = new HashMap<>();
        map.put("ethAddress", fromAddress);
        map.put("chainID", chainID + "");
        TCRequestUtil.postCommonRequest(TAG, "eth/ethGetTransactionCount",
                map, new TCRequestUtil.TCRequestCallback() {
                    @Override
                    public void onSuccess(String response) {
                        JSONObject jsonObject = null;
                        try {
                            jsonObject = JSONObject.fromObject(response);
                            Log.i("transersuccess", response);
                            if (jsonObject.has("result") && jsonObject.getString("result").equals("success")) {
                                final BigInteger nonce = BigInteger.valueOf(jsonObject.getInt("transactionCount"));
                                callBack.nonceResult(true, nonce);
                            } else {
                                callBack.nonceResult(false, null);

                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            callBack.nonceResult(false, null);
                        }
                    }

                    @Override
                    public void onFail(String error) {
                        callBack.nonceResult(false, null);
                    }
                });
    }

    /**
     * 获取账号交易次数 nonce
     * 同步方法
     *
     * @param fromAddress 付款地址
     * @param chainID     链id
     * @return
     */
    public BigInteger getTransactionNonceSync(String tag, String fromAddress, int chainID) throws Exception {
        HashMap<String, String> map = new HashMap<>();
        map.put("ethAddress", fromAddress);
        map.put("chainID", chainID + "");

        String requestUrl = TCRequestUtil.BASE_URL + "/eth/ethGetTransactionCount";
        Response response = JFCommonRequestManager.getInstance().requestPostBySyn(tag, requestUrl, map);
        if (response != null && response.isSuccessful()) {
            String result = response.body().string();
            org.json.JSONObject jsonObject = new org.json.JSONObject(result);
            if (jsonObject.optString("result", "").equals("success")) {
                return new BigInteger(jsonObject.optString("transactionCount", "0"));
            }
            throw new Exception("获取nonce失败");
        } else {
            throw new Exception("获取nonce失败");
        }

    }

    /**
     * ETH 代币转账 智能合约转账
     *
     * @param nonce            交易次数
     * @param fromAddress      转账地址
     * @param fromUserId       转账人imId
     * @param toAddress        收账地址
     * @param toUserId         收账人ID
     * @param amount           转账金额
     * @param etherAmount      输入的转账金额
     * @param credentials      证书
     * @param gasPrice         矿工费
     *                         //     * @param gasLimit
     *                         //     * @param type             交易类型 1是普通账户间转账 2是普通账号调用智能合约（默认值1）
     * @param constractAddress 智能合约地址
     * @param chainId          所属链编号（默认值是2 以太链）
     * @param tokenId          代币编号
     *                         //@param tokenValue       代币值
     * @param tokenUnit        代币单位
     * @param callBack
     */
    public void startEthTokenTransaction(BigInteger nonce, String fromAddress,
                                         String fromUserId, String toAddress,
                                         String toUserId, BigInteger amount,
                                         String etherAmount,
                                         Credentials credentials, BigInteger gasPrice,
                                         // BigInteger gasLimit, String type,
                                         String constractAddress, byte chainId,
                                         String tokenId,
                                         String tokenUnit, final TransactionCallBack callBack) {

//        //todo 之前手续费太低，现在手续费需要乘以43倍左右,跟imtoken 对应
//        gasPrice = gasPrice.multiply(new BigInteger("43"));
        Log.e("yqs-constractGasprice",gasPrice.toString());


        toAddress = toAddress.toLowerCase();
        String hexValue = getTokenTransferSignHex(nonce, toAddress, amount, credentials, gasPrice, constractAddress);


        //接口调用参数
        HashMap<String, String> map = new HashMap<>();
        map.put("fromUserId", fromUserId);
        map.put("fromEthAddress", fromAddress);
        map.put("toUserId", toUserId);
        map.put("toEthAddress", toAddress);
        //  map.put("value", amount.toString());
        map.put("signedTransactionData", hexValue);
        map.put("type", "2");
        map.put("constractAddress", constractAddress);
        map.put("chainId", chainId + "");
        map.put("tokenId", tokenId);
        map.put("tokenValue", etherAmount);
        map.put("tokenUnit", tokenUnit);

        LogUtils.d(map.toString());

        //调用接口进行交易
        TCRequestUtil.getCommonRequest(TAG, "chain/ethSendRawTransaction",
                map, new TCRequestUtil.TCRequestCallback() {
                    @Override
                    public void onSuccess(String response) {

                        try {
                            JSONObject jsonObject = JSONObject.fromObject(response);
                            LogUtils.i(jsonObject.toString());
                            if (jsonObject.containsKey("result") && "success".equals(jsonObject.getString("result"))) {
                                if (jsonObject.has("transactionHash")) {
                                    //交易成功，返回交易编号
                                    callBack.transactionResult(true, jsonObject.toString());
                                } else {
                                    callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                                }
                            } else {
                                callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                        }
                    }

                    @Override
                    public void onFail(String error) {
                        callBack.transactionResult(false, ResStringUtil.getString(R.string.open_planet_transaction_fail));
                    }
                });
    }

    /**
     * 获取代币交易签名（智能合约））
     *
     * @param nonce
     * @param toAddress
     * @param amount
     * @param credentials
     * @param gasPrice
     * @param constractAddress 合约地址
     * @return
     */
    public String getTokenTransferSignHex(BigInteger nonce, String toAddress, BigInteger amount,
                                          Credentials credentials, BigInteger gasPrice, String constractAddress) {
        //交易签名
        String hexValue = null;

        toAddress = toAddress.toLowerCase();

        LogUtils.d("MONEY", amount);
        //方法名 交易
        String methodName = "transfer";
        //输入参数
        List<Type> inputParameters = new ArrayList<>();
        //对方账户地址
        Address tAddress = new Address(toAddress);
        //转账代币 金额

        Uint256 value = new Uint256(amount);
        inputParameters.add(tAddress);
        inputParameters.add(value);

        //一个空集合
        List<TypeReference<?>> outputParameters = new ArrayList<>();
        TypeReference<Bool> typeReference = new TypeReference<Bool>() {
        };
        outputParameters.add(typeReference);

        //创建function
        Function function = new Function(methodName, inputParameters, outputParameters);
        //获取data
        String data = FunctionEncoder.encode(function);
        LogUtils.i("data:" + data);
        //创建原始交易
//        BigInteger gasLimit = new BigInteger(Contract.GAS_LIMIT.longValue() / 43 + "");
        BigInteger gasLimit = Contract.GAS_LIMIT;

        RawTransaction transaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, constractAddress, data);

        //签名Transaction，这里要对交易做签名
        byte[] signedMessage = null;
//        if (chainId > ChainId.NONE) {
//            signedMessage = TransactionEncoder.signMessage(transaction, chainId, credentials);
//        } else {
        signedMessage = TransactionEncoder.signMessage(transaction, credentials);
//        }
//        //这里我们拿到了交易的签名
        hexValue = Numeric.toHexString(signedMessage);
//        Function function = new Function(
//                "transfer",
//                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(toAddress),
//                        new org.web3j.abi.datatypes.generated.Uint256(amount)),
//                Collections.<TypeReference<?>>emptyList());
//
//        String data = FunctionEncoder.encode(function);

        // RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, Contract.GAS_LIMIT, constractAddress, amount, data);

//        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, Contract.GAS_LIMIT, constractAddress, data);
//
//
//        // // sign & send our transaction
//        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
//        hexValue = Numeric.toHexString(signedMessage);

        LogUtils.i("hexValue:" + hexValue);
        return hexValue;
    }

}
